package com.tender.desensitization.factory.field;

import com.tender.desensitization.FieldReflectUtils;
import org.apache.commons.lang3.ClassUtils;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.tender.desensitization.GlobalRequestResponseAdvice.circleMap;

/**
 * 需要考虑 类型循环嵌套的问题：eg
 *
 * public class NameCodeVO {
 *     private List<NameCodeVO> children;
 * }
 *
 * eg:
 * 顶级 Class<?> 嵌套
 *
 * public class Stu {
 *     private Teacher teacher;
 * }
 * public class Teacher {
 *     List<Stu> stuList;
 * }
 */

public class FieldAnnotationCollectionDetectionFactory implements FieldAnnotationDetectionFactory {

    private FieldAnnotationDetectionFactoryFacade factoryFacade;

    public FieldAnnotationCollectionDetectionFactory(FieldAnnotationDetectionFactoryFacade factoryFacade) {
        this.factoryFacade = factoryFacade;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return List.class.isAssignableFrom(clazz)
                || Set.class.isAssignableFrom(clazz)
                || Map.class.isAssignableFrom(clazz);
    }

    @Override
    public boolean processType(Field field) {
        if (null == field) {
            return false;
        }

        Class<?> resolve = FieldReflectUtils.getGenericClassWithInCollection(field);

        // 针对基本类型 和 String 类型的，直接 false
        if (null == resolve || ClassUtils.isPrimitiveOrWrapper(resolve) || String.class == resolve) {
            return false;
        }

        List<Field> allFields = FieldReflectUtils.getAllFields(resolve);

        if (!CollectionUtils.isEmpty(allFields)) {
            List<Class<?>> classList = circleMap.get(TopClassContext.getTopClass());
            // 存在类型嵌套
            if (classList.contains(resolve)) {
                return false;
            } else {
                classList.add(resolve);
            }
        }

        for (Field item : allFields) {
            FieldAnnotationDetectionFactory processFactory = factoryFacade.getProcessFactory(item.getType());
            if (null != processFactory) {
                if (processFactory.processType(item)) {
                    return true;
                }
            }
        }

        return false;
    }

}